$(function(){
  var dics = {'en':{},'la':{}};
  var queue = {'en':[],'la':[]};
  var callbacks = [];
  var addEntries = function(lang,entries) {
    if(typeof(entries)=='undefined') {
      entries = lang;
      lang = 'en';
    }
    var dic = dics[lang];
    if(dic) {
      entries = entries.split(' ');
      entries.forEach(function(o,i) {
        entries[i] = o.toLowerCase();
        var word = o.replace(/[~-]/g,'').toLowerCase();
        dic[word] = o;
      });
      if(lang=='en') {
        var cb;
        while(cb = callbacks.shift()) {
          cb();
        }
      }
    }
  }
  var processQueue = function(callback) {
    var q = queue.en;
    if(q.length) {
      if(typeof(callback)=='function') {
        callbacks.push(callback);
      }
      $.getScript('http://apps.illuminarepublications.com/dev-beta/syl.php?txt=' + q.splice(0).join(' '));
    }
  };
  var regexWord = /\S+/g;
  var regexAlpha = /\w+/g;
  var regexNonAlpha = /\W+/g;
  var defaultOptions = {
    delimiter: '+',
    lang: 'en',
    splitAfterHyphens: false,
    splitConsumesHyphens: false,
    delimiterAtEnd: false
  };
  var optLilypond = {
    delimiter: ' -- ',
    splitAfterHyphens: false,
    splitConsumesHyphens: true,
    delimiterAtEnd: false
  }
  var optGregorio = {
    delimiter: '()',
    splitAfterHyphens: true,
    splitConsumesHyphens: false,
    delimiterAtEnd: true
  }
  
  var magic = function(subject,options,callback) {
    var addDelimiter = function(text) {
      if(!options.delimiterAtEnd) return text;
      return (text.slice(-options.delimiter.length) == options.delimiter)?
        text : (text + options.delimiter);
    }
    var newInQueue = false;
    if(typeof(options)=='function') callback = options;
    options = $.extend({},defaultOptions,options);
    subject = subject.replace(/\b\+\b/g,options.delimiter);
    if(!(options.lang in dics)) options.lang = defaultOptions.lang;
    var dic = dics[options.lang];
    var escapedDelimiter = options.delimiter.replace(/./g,function(m){return '\\'+m});
    var delimiterRegex = new RegExp(escapedDelimiter,'g');
    var delimitedWordRegex = new RegExp('\\S+(' + escapedDelimiter + '\\S+)*','g');
    var localDic = {};
    var result = subject
      .replace(delimitedWordRegex,function(m){
        var word = m.replace(delimiterRegex,'');
        if(word != m) {
          localDic[word] = addDelimiter(m);
        }
        return word;
      })
      .replace(regexWord,function(m,i) {
        if(localDic[m]) return localDic[m];
        var word = m.replace(regexNonAlpha,'');
        var dicWord = dic[word];
        if(!dicWord) {
          dicWord = dic[word.toLowerCase()];
        }
        if(!dicWord) {
          if(m.indexOf('-') < 0) {
            var q = queue[options.lang];
            var word = word.toLowerCase();
            if(q.indexOf(word)<0) {
              q.push(word);
              newInQueue = true;
            }
            return addDelimiter(m);
          }
          var result = m.replace(/[^-]+/g,function(m,i) {
            return magic(m,options);
          });
          if(options.splitConsumesHyphens) {
            result = result.replace(/-+/g,options.delimiter);
          } else if(options.splitAfterHyphens) {
            result = result.replace(/-+/g,'-' + options.delimiter);
          }
          return addDelimiter(result);
        }
        var dontTouchM = false;
        for(var i=0,dicI=0; i<m.length; ++i,++dicI) {
          var character = m[i],
              dicCharacter = dicWord[dicI] || '',
              nonAlpha = character.match(regexNonAlpha),
              dicNonAlpha = dicCharacter.match(regexNonAlpha);
          if(nonAlpha && dicNonAlpha) {
            if(nonAlpha == '-') {
              dicWord = dicWord.slice(0,dicI) + character + dicWord.slice(1+dicI);
            } else {
              if(dontTouchM) {
                dicWord = dicWord.slice(0,dicI) + character + dicNonAlpha + dicWord.slice(1+dicI);
              } else {
                dicWord = dicWord.slice(0,dicI) + dicNonAlpha + character + dicWord.slice(1+dicI);
              }
              ++dicI;
            }
          } else if(nonAlpha) {
            dicWord = dicWord.slice(0,dicI) + character + dicWord.slice(dicI);
          } else if(dicNonAlpha) {
            if(dontTouchM) {
              dicWord = dicWord.slice(0,dicI) + dicWord.slice(1+dicI);
            } else {
              --i;
            }
          } else {
            if(dicCharacter != character) {
              dicWord = dicWord.slice(0,dicI) + character + dicWord.slice(1+dicI);
            }
          }
          if(character == '|') dontTouchM = !dontTouchM;
        }
        if(options.splitConsumesHyphens) {
          dicWord = dicWord.replace(/-/g,options.delimiter);
        } else if(options.splitAfterHyphens) {
          dicWord = dicWord.replace(/-/g,'-' + options.delimiter);
        }
        if(options.delimiterAtEnd) {
          dicWord += options.delimiter;
        }
        return dicWord.replace(/~/g,options.delimiter);
      });
    processQueue(callback);
    return result;
  };
  window.Dic = {
    syllabify: magic,
    addEntries: addEntries,
    lilypond: optLilypond,
    gregorio: optGregorio
  };
  for(lang in dics) {
    $.getScript('http://apps.illuminarepublications.com/dev-beta/dictionary.php?lang=' + lang);
  }
});
